﻿using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Profiling;
using Yoozoo.Framework.Core;
using Yoozoo.Managers.ResourceManagerV2.Runtime;

namespace Yoozoo.Modules.Download
{
    /// <summary>
    /// 用来管理下载，限制下载数量，维护一个有优先级的待下载列表
    /// 不处理出错重试
    /// </summary>
    public class DownloadManager : SingletonMono<DownloadManager>
    {
        public static DownloadManager Instance => GetInstance("Download");

        /// <summary>
        /// 同时开启的WebRequest上限
        /// </summary>
        public int Limit = 4;

        public int BufferSize = 1024 * 1024;

        /// <summary>
        /// Request对象池
        /// </summary>
        private readonly UnityWebRequestDownloadPool unityWebRequestDownloadPool = new UnityWebRequestDownloadPool(16);

        /// <summary>
        /// 传入DownloadHandlerScript的Buffer，1mb
        /// DownloadHandlerScript本身没有Reset方法，不能用对象池，只能对buffer创建对象池
        /// </summary>
        private PreAllocatedBufferPool preAllocatedBufferPool;

        /// <summary>
        /// 待下载队列，有优先级
        /// </summary>
        private readonly PriorityQueue<DownloadInfo> downloadQueue = new PriorityQueue<DownloadInfo>(2048);

        /// <summary>
        /// 下载中的队列
        /// </summary>
        private readonly List<DownloadInfo> downloadingList = new List<DownloadInfo>(16);

        /// <summary>
        /// 下载的字节数，用于计算下载速度
        /// </summary>
        private ulong downloadingSize;

        private bool isActive = true;

        public bool IsActive()
        {
            return isActive;
        }

        /// <summary>
        /// 下载速度（字节/秒)
        /// </summary>
        public float Speed;

        /// <summary>
        /// 30帧平均
        /// </summary>
        private const int SpeedCount = 30;

        /// <summary>
        /// 用于平均下载速度，获取秒速而不是帧速
        /// </summary>
        private float[] speeds = new float[SpeedCount];

        private int speedIndex = 0;
        
#if DOWNLOAD_DEBUG
   
        #region 调试功能
        // 以下调试功能，待删除
        public int fontSize = 30;

        private int downloadCount;

        private int successCount;

        private int failedCount;

        private string lastErrorMessage;

        void OnGUI()
        {
            GUIStyle localStyle = new GUIStyle(GUI.skin.label);
            localStyle.normal.textColor = Color.red;
            localStyle.fontSize = fontSize;

            string network = (Application.internetReachability == NetworkReachability.NotReachable)
                ? "Disconnect"
                : "Connect";

            var rect = new Rect(50, 50, 1200, 100);
            GUI.Label(rect, $"Downloading: {downloadingList.Count} Waiting: {downloadQueue.Count}", localStyle);
            rect.y += 30;
            GUI.Label(rect, $"Success: {successCount} Failed: {failedCount}, Total: {downloadCount}", localStyle);
            rect.y += 30;
            GUI.Label(rect, $"Speed: {GetByteLengthString((long)Speed)}/S Network: {network}", localStyle);
            rect.y += 30;
            GUI.Label(rect, $"Last error: {lastErrorMessage}", localStyle);
            rect.y += 30;
            GUI.Label(rect, $"Req pool: {unityWebRequestDownloadPool.GetTotalCount()} Buffer pool: {preAllocatedBufferPool.GetTotalCount()}", localStyle);
            rect.y += 30;
            GUI.Label(rect, $"Req limit: {Limit} Buffer size: {BufferSize}", localStyle);
        }

        private string GetByteLengthString(long byteLength)
        {
            if (byteLength < 1024L) // 2 ^ 10
            {
                return $"{byteLength} Bytes";
            }

            if (byteLength < 1048576L) // 2 ^ 20
            {
                return $"{(byteLength / 1024f):F2} KB";
            }

            if (byteLength < 1073741824L) // 2 ^ 30
            {
                return $"{(byteLength / 1048576f):F2} MB";
            }

            if (byteLength < 1099511627776L) // 2 ^ 40
            {
                return $"{(byteLength / 1073741824f):F2} GB";
            }

            if (byteLength < 1125899906842624L) // 2 ^ 50
            {
                return $"{(byteLength / 1099511627776f):F2} TB";
            }

            if (byteLength < 1152921504606846976L) // 2 ^ 60
            {
                return $"{(byteLength / 1125899906842624f):F2} PB";
            }

            return $"{(byteLength / 1152921504606846976f):F2} EB";
        }

        #endregion
             
#endif

        /// <summary>
        /// 根据设备等级设置最大下载个数
        /// </summary>
        /// <param name="deviceGrade"></param>
        public void SetLimitByDeviceGrade(DevicesGrading.DeviceGrade deviceGrade)
        {
            switch (deviceGrade)
            {
                case DevicesGrading.DeviceGrade.LOW:
                case DevicesGrading.DeviceGrade.MEDIUM:
                    Limit = 1;
                    BufferSize = 1024 * 1024;
                    break;
                case DevicesGrading.DeviceGrade.HIGH:
                    Limit = 2;
                    BufferSize = 1024 * 1024;
                    break;
                case DevicesGrading.DeviceGrade.VERYHIGH:
                case DevicesGrading.DeviceGrade.PERFECT:
                    Limit = 4;
                    BufferSize = 1024 * 1024;
                    break;
                default:
                    Limit = 1;
                    BufferSize = 1024 * 1024;
                    break;
            }

            Log.Info("SetLimitByDeviceGrade: {0} Limit: {1} Buffer size: {2}", deviceGrade, Limit, BufferSize);
        }

        public override void Initialize(Options options = null)
        {
            base.Initialize(options);

            unityWebRequestDownloadPool.OnInit(Limit);
            // 这里开了4mb内存，不用不要初始化，用完了要销毁
            preAllocatedBufferPool = new PreAllocatedBufferPool(16, BufferSize);
            preAllocatedBufferPool.OnInit(Limit);
            Log.Info("Initialize: Limit: {0} Buffer size: {1}",  Limit, BufferSize);
        }

        public override void Dispose()
        {
            unityWebRequestDownloadPool.Destroy();
            preAllocatedBufferPool.Destroy();
            downloadingList.Clear();

            base.Dispose();
        }

        /// <summary>
        /// 开始下载 （实际是加入列表，并没有真正开始）
        /// </summary>
        /// <param name="url"></param>
        /// <param name="path"></param>
        /// <param name="length"></param>
        /// <param name="priority"></param>
        /// <param name="onFinished"></param>
        /// <returns></returns>
        public DownloadInfo StartDownload(string url, string path, ulong length, int priority, Action<bool> onFinished)
        {
            // 这里的DownloadInfo会传给外部，不能用对象池管理
            var downloadInfo = new DownloadInfo
            {
                Path = path,
                TotalSize = length,
                Url = url,
                Priority = priority,
                OnFinished = onFinished
            };

            downloadQueue.Push(downloadInfo);
            return downloadInfo;
        }

        /// <summary>
        /// 对下载速度进行平均，避免显示时波动过大
        /// </summary>
        /// <param name="currentSpeed"></param>
        private void SetSpeed(float currentSpeed)
        {
            speeds[speedIndex] = currentSpeed;
            speedIndex++;
            if (speedIndex == SpeedCount)
                speedIndex = 0;

            Speed = 0;
            foreach (float speed in speeds)
            {
                Speed += speed / SpeedCount;
            }
            
        }

        /// <summary>
        /// 每帧处理结束的下载，开启新的下载
        /// </summary>
        public void Update()
        {
            // 检查 正在下载的文件是否下载结束，进行回调并移出队列
            int downloadingCount = downloadingList.Count;

            ulong downloadingSizeNow = 0;
            ulong downloadedSize = 0;
#if UWA
            UWAEngine.PushSample("Download.Finish");
#endif
            // 是否执行过任何回调
            bool doAnyCallback = false;
            //
            for (int i = downloadingCount - 1; i >= 0; i--)
            {
                var downloadInfo = downloadingList[i];
                downloadingSizeNow += downloadInfo.CurrentSize;
                
                if (downloadInfo.State == DownloadState.Done)
                {
                    //UnityEngine.Debug.LogErrorFormat("DownloadManager.Update: Download success {0}, {1}, {2}", downloadInfo.Path, downloadInfo.CurrentSize, downloadInfo.OnFinished);
#if DOWNLOAD_DEBUG
                    successCount++;
#endif
                    Profiler.BeginSample("Download.OnSuccess");
                    downloadInfo.OnFinished?.Invoke(true);
                    Profiler.EndSample();
                    doAnyCallback = true;
                }
                else if(downloadInfo.State == DownloadState.Error)
                {
#if DOWNLOAD_DEBUG
                    failedCount++;
                    lastErrorMessage = string.IsNullOrEmpty(downloadInfo.ErrorMessage) ? "Unknown" : downloadInfo.ErrorMessage;
#endif
                    Profiler.BeginSample("Download.OnError");
                    downloadInfo.OnFinished?.Invoke(false);
                    Profiler.EndSample();
                    doAnyCallback = true;
                }
                else
                {
                    continue;
                }

                Profiler.BeginSample("Download.Recycle");
                var lastIndex = downloadingList.Count - 1;
                downloadingList[i] = downloadingList[lastIndex];
                downloadingList.RemoveAt(lastIndex);

                downloadedSize += downloadInfo.CurrentSize;

                // Request回池
                unityWebRequestDownloadPool.RecoverItem(downloadInfo.UnityWebRequestDownload);
                preAllocatedBufferPool.RecoverItem(downloadInfo.Buffer);
                Profiler.EndSample();
            }
            //Profiler.EndSample();
#if UWA
            UWAEngine.PopSample();
#endif
            // 统计本帧的下载量，计算下载速度
            ulong deltaSize = downloadingSizeNow - downloadingSize;
            downloadingSize = downloadingSizeNow - downloadedSize;
            SetSpeed(deltaSize / Time.deltaTime);

            // 网络连接不上，不新开下载
            if (Application.internetReachability == NetworkReachability.NotReachable)
            {
                return;
            }

            if (!isActive)
            {
                return;
            }

            // 执行过任何回调就别开新下载了
            if (doAnyCallback)
            {
                return;
            }

            // 从待下载队列中添加新的下载

            if (downloadingList.Count >= Limit || downloadQueue.Count == 0)
                return;

            //Profiler.BeginSample("Download.Start");
#if UWA
            UWAEngine.PushSample("Download.Start");
#endif
            // Request.Start GC.Alloc较大，改成一帧只创建一个
            //int count = Math.Min(downloadQueue.Count, Limit - downloadingList.Count);
            //for (int i = 0; i < count; i++)
            
            Profiler.BeginSample("StartDownloadRequest");
            {
#if DOWNLOAD_DEBUG
                downloadCount++;
#endif
                var downloadInfo = downloadQueue.Pop();
                var downloadRequest = unityWebRequestDownloadPool.GetItem();
                var buffer = preAllocatedBufferPool.GetItem();
                downloadRequest.Start(downloadInfo, buffer);
                downloadingList.Add(downloadInfo);
            }
            Profiler.EndSample();
            //Profiler.EndSample();
#if UWA
            UWAEngine.PopSample();
#endif
        }

        public void Pause()
        {
            isActive = false;
        }

        public void Resume()
        {
            isActive = true;
        }

        /// <summary>
        /// 是否空闲，用于判断是否可以销毁
        /// </summary>
        /// <returns></returns>
        public bool IsIdle()
        {
            if (downloadQueue.Count > 0)
                return false;

            if (downloadingList.Count > 0)
                return false;

            return true;
        }
    }
}
